using System;
using System.Collections;
using System.Collections.ObjectModel;
using System.Linq;

using EMan.BL.Properties;
using EMan.Common.EnterpriseStructure;
using EMan.Interface.Common;
using EMan.Interface.EnterpriseStructure;
using EMan.Interface.EnterpriseStructure.Departments;
using EMan.Interface.EnterpriseStructure.Employees;

using TrueCode.Core.Aspects;
using TrueCode.Core.Diagnostics;
using TrueCode.Core.EditableEntity;

namespace EMan.BL.EnterpriseStructure.StructureTree
{
  [UsedImplicitly]
  public class StructureTreeEditorPresenter : Presenter< IStructureTreeEditorView >, IStructureTreeEditorPresenter
  {
    private readonly IEnterpriseStructure _enterpriseStructure;
    private bool _createDepartmentEnabled = true;
    private bool _deleteDepartmentEnabled;
    private bool _createEmployeeEnabled;
    private bool _deleteEmployeeEnabled;
    private bool _editEnabled;
    private bool _saveEnabled;
    private bool _isEnabled = true;

    private ObservableCollection< DepartmentEditable > _departments;
    private IStructureTreeNode _selectedNode;

    public StructureTreeEditorPresenter( [NotNull] IEnterpriseStructure enterpriseStructure, [NotNull] IDiagnostic diagnostic ) : base( diagnostic )
    {
      if( enterpriseStructure == null ) throw new ArgumentNullException( "enterpriseStructure" );

      _enterpriseStructure = enterpriseStructure;

      CurrentState = State.Ready;

      _departments = new ObservableCollection< DepartmentEditable >( );

      var executor = new ActivityExecutor( this, new Activity( "Loading", "Loading", true ),
        () => IsEnabled = false,
        () => IsEnabled = true );

      executor.ExecuteAsync(
        () => _enterpriseStructure.GetAllDepartments(),
        result =>
          {
            _departments = new ObservableCollection< DepartmentEditable >();//( result.Select( d => new DepartmentEditable( d ) ) );
            foreach( var department in result )
            {
              var departmentEditable = new DepartmentEditable( department );
              foreach( var employee in department.Employees )
                departmentEditable.Attach( new EmployeeEditable( employee ) );
              _departments.Add( departmentEditable );
            }
            RaisePropertyChanged( () => Departments );
         } );
    }

    public override void Dispose(){}

    #region Implementation of IStructureTreeEditorPresenter
    public IEnumerable Departments
    {
      get { return _departments; }
    }

    public bool IsCreateDepartmentEnabled
    {
      get { return _createDepartmentEnabled; }
      private set
      {
        if( _createDepartmentEnabled != value )
        {
          _createDepartmentEnabled = value;
          RaisePropertyChanged( () => IsCreateDepartmentEnabled );
        }
      }
    }

    public bool IsDeleteDepartmentEnabled
    {
      get { return _deleteDepartmentEnabled; }
      private set
      {
        if( _deleteDepartmentEnabled != value )
        {
          _deleteDepartmentEnabled = value;
          RaisePropertyChanged( () => IsDeleteDepartmentEnabled );
        }
      }
    }

    public bool IsCreateEmployeeEnabled
    {
      get { return _createEmployeeEnabled; }
      private set
      {
        if( _createEmployeeEnabled != value )
        {
          _createEmployeeEnabled = value;
          RaisePropertyChanged( () => IsCreateEmployeeEnabled );
        }
      }
    }

    public bool IsDeleteEmployeeEnabled
    {
      get { return _deleteEmployeeEnabled; }
      private set
      {
        if( _deleteEmployeeEnabled != value )
        {
          _deleteEmployeeEnabled = value;
          RaisePropertyChanged( () => IsDeleteEmployeeEnabled );
        }
      }
    }

    public bool IsEditEnabled
    {
      get { return _editEnabled; }
      private set
      {
        if( _editEnabled != value )
        {
          _editEnabled = value;
          RaisePropertyChanged( () => IsEditEnabled );
        }
      }
    }

    public bool IsSaveEnabled
    {
      get { return _saveEnabled; }
      private set
      {
        if( _saveEnabled != value )
        {
          _saveEnabled = value;
          RaisePropertyChanged( () => IsSaveEnabled );
        }
      }
    }

    public bool IsEnabled
    {
      get { return _isEnabled; }
      private set
      {
        if( _isEnabled != value )
        {
          _isEnabled = value;
          RaisePropertyChanged( () => IsEnabled );
        }
      }
    }

    public object SelectedItem
    {
      get { return _selectedNode; }
      set
      {
        _selectedNode = (IStructureTreeNode)value;

        IsCreateDepartmentEnabled = SelectedEmployeeNode == null && ( SelectedDepartmentNode == null || SelectedDepartmentNode.State != EditableObjectState.Deleted );
        IsCreateEmployeeEnabled   = SelectedDepartmentNode != null && SelectedDepartmentNode.State != EditableObjectState.Deleted;
        IsDeleteDepartmentEnabled = SelectedDepartmentNode != null && SelectedDepartmentNode.State != EditableObjectState.Deleted;
        IsDeleteEmployeeEnabled   = SelectedEmployeeNode != null;
        IsEditEnabled             = _selectedNode != null;
      }
    }

    public void CreateDepartment()
    {
      var newDepartment = new Department( "New department" );

      var departmentEditable = new DepartmentEditable( newDepartment, true );

      if( View.EditDepartment( departmentEditable ) )
        _departments.Add( departmentEditable );
    }

    public void CreateEmployee()
    {
      var newEmployee = new Employee( null, SelectedDepartmentNode.Entity, null, null, null );

      var employeeEditable = new EmployeeEditable( newEmployee, true );
      if( View.EditEmployee( employeeEditable ) )
        SelectedDepartmentNode.Add( employeeEditable );
    }

    public void EditEntity()
    {
      if( SelectedDepartmentNode != null )
        EditDepartment( SelectedDepartmentNode );
    }

    public void Delete()
    {
      if( SelectedDepartmentNode != null )
        DeleteDepartment( SelectedDepartmentNode );
    }

    public void Save()
    {
      var executor = new ActivityExecutor( this,
                                           new Activity( Resources.EditStructurePresenter_ActivitySave_Title,
                                                         Resources.EditStructurePresenter_ActivitySave_GatheringData ),
                                           () => IsEnabled = false,
                                           () => IsEnabled = true );
      executor.ExecuteAsync( () =>
                               {
                                 var changeset = StructureChangesetManager.MakeChangeset( _departments );
                                 executor.Activity.Details = Resources.EditStructurePresenter_ActivitySave_Publishing;
                                 return _enterpriseStructure.UpdateStructure( changeset );
                               },

                             result => StructureChangesetManager.MergeChangeset( result, _departments ) );
    }
    #endregion

    private DepartmentEditable SelectedDepartmentNode
    {
      get { return _selectedNode as DepartmentEditable; }
    }

    private EmployeeNode SelectedEmployeeNode
    {
      get{ return _selectedNode as EmployeeNode; }
    }

    private void DeleteDepartment( DepartmentEditable departmentEditable )
    {
      if( departmentEditable.State == EditableObjectState.New )
        _departments.Remove( departmentEditable );
      else if( !departmentEditable.Employees.Any() ||
            Answer.Yes ==
            View.AskConfirmaiton( string.Format( Resources.EditStructurePresenter_Question_DeleteDepartmentTemplate,
                                                 departmentEditable.Title ),
                                  MessageType.Question,
                                  PossibleAnswers.YesNo ) )
          departmentEditable.MarkDeleted();
    }

    private void EditDepartment( DepartmentEditable departmentEditable )
    {
      using( var editSession = departmentEditable.StartNewEditSession() )
      {
        if( View.EditDepartment( editSession ) )
          editSession.AcceptChanges();
      }
    }
  }
}